package netty.dubbo.client.discovery;

import netty.dubbo.api.ZkConstant;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.retry.ExponentialBackoffRetry;

import java.util.List;

/**
 * @author: 程序员七哥
 * @date: 2021-12-19
 * @description: 默认服务发现实现类，从 zk 注册中心获取
 */
public class DefaultServiceDiscovery implements ServiceDiscovery{
    // zk 客户端
    private CuratorFramework curator;
    // 服务提供方地址列表
    private List<String> servers;

    public DefaultServiceDiscovery() {
        curator = CuratorFrameworkFactory.builder()
                .connectString(ZkConstant.ZK_CLUSTER)
                // 连接超时时间，默认 15 秒
                .connectionTimeoutMs(10000)
                // 会话超时时间，默认 60 秒
                .sessionTimeoutMs(3000)
                // 设置重试机制：每1秒重试一次，最多重试10次
                .retryPolicy(new ExponentialBackoffRetry(1000, 10))
                .build();
        curator.start();
    }

    @Override
    public String discovery(String serviceName) throws Exception {
        // 定义服务名对应的 zk 节点
        String  servicePath = ZkConstant.ZK_DUBBO_ROOT_PATH + "/" + serviceName;
        // 获取服务名下所有提供方地址
        servers = curator.getChildren().forPath(servicePath);
        if (servers.size() == 0) {
            return null;
        }
        // 添加 watcher 监听，监听子节点下列表的变化（服务提供方的变化)）
        registerWatcher(servicePath);
        return new RandomLoadBalance().load(servers);
    }

    private void registerWatcher(String servicePath) throws Exception {
        // 其会将指定路径下的节点数据内容及状态缓存到本地
        PathChildrenCache cache = new PathChildrenCache(curator, servicePath, true);
        // 一旦监听到子节点列表发生变化，马上更新当前的servers集合
        cache.getListenable().addListener((client,event) -> {
            servers = curator.getChildren().forPath(servicePath);
        });
        // 启动监听
        cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);

    }
}
